package net.rudp;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.ArrayList;
import java.util.HashMap;

import com.comunity_cloud.exceptions.TransportException;
import com.comunity_cloud.rest.RestClient;


import net.rudp.impl.Segment;

public class SinglePortReliableSocket extends ReliableSocket {

	private static HashMap<SocketAddress, SinglePortReliableSocket>   _clientSockTable = new HashMap<>();
	
	private static boolean socketCreated = false;
	
	private static DatagramSocket _Sock;
	
	private static ReceiverThread receiverThread = new ReceiverThread();
	
	public SinglePortReliableSocket(String host, int port) throws IOException {
		super(socketCreated?_Sock:new DatagramSocket(new InetSocketAddress(0)));
		synchronized (receiverThread) {
			if (!socketCreated) {
				_Sock = _sock;
				try {
					RestClient.setConnectPort(_Sock.getLocalPort());
				} catch (TransportException e) {
					e.printStackTrace();
				}
				receiverThread.start();
				socketCreated = true;
			}
		}
		InetSocketAddress endpoint = new InetSocketAddress(host, port);
		
		synchronized (_clientSockTable) {
			_clientSockTable.put(endpoint, this);
		}
		connect(endpoint);
	}
	
	protected void init(DatagramSocket sock, ReliableSocketProfile profile)
	{
		_queue = new ArrayList<Segment>();
		super.init(sock, profile);
		
	}

	protected Segment receiveSegmentImpl()
	{
		synchronized (_queue) {
			while (_queue.isEmpty()) {
				try {
					_queue.wait();
				}
				catch (InterruptedException xcp) {
					xcp.printStackTrace();
				}
			}

			return (Segment) _queue.remove(0);
		}
	}

	protected void segmentReceived(Segment s)
	{
		synchronized (_queue) {
			_queue.add(s);
			_queue.notify();
		}
	}

	protected void closeSocket()
	{
		synchronized (_queue) {
			_queue.clear();
			_queue.add(null);
			_queue.notify();
		}
	}
	
	 protected void log(String msg)
     {
         System.out.println(getPort() + ": " + msg);
     }

     private ArrayList<Segment> _queue;
     
     
     
     private static class ReceiverThread extends Thread
     {
         public ReceiverThread()
         {
             super("SinglePortReliableSocket");
             setDaemon(true);
         }

         public void run()
         {
             byte[] buffer = new byte[65535];

             while (true) {
                 DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
                 SinglePortReliableSocket sock = null;

                 try {
                     _Sock.receive(packet);
                     SocketAddress endpoint = packet.getSocketAddress();
                     Segment s = Segment.parse(packet.getData(), 0, packet.getLength());
//                     System.out.println("ClientSocket received packet "+s.getClass().getSimpleName());
                     synchronized (_clientSockTable) {

                         if (!_clientSockTable.containsKey(endpoint)) {
                        	 continue;
                         }
                         sock =  _clientSockTable.get(endpoint);
                     }

                     if (sock != null) {
                         sock.segmentReceived(s);
                     }
                 }
                 catch (IOException xcp) {
                     xcp.printStackTrace();
                 }
             }
         }
     }
     
   
}
